﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using Microsoft.Adapter.SAP;
namespace SAPInvoker.Reflector
{
    public class RFCParametersDiscover
    {
        private static Assembly ass = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "//Microsoft.Adapter.SAP.SAPProvider.dll");

        public Type GetTypeFromSAPAdapter(string typeName)
        {
            if (ass != null)
            {
                Type t = ass.GetType(typeName);
                return t;
            }
            return null;
        }

        public  RFCParametersInfo GetRFCParametersInfo(string rfcName,SAPConnection conn)
        {
            Type tInvoke = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.SAPInvoker");
            Type tRFCParameterCollection = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.RFCParameterCollection");
            Type tSAPStructureType = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.SAPStructureType");
            Type tRFCParameter = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.RFCParameter");
            Type tRFCTable = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.SAPTable");
            Type tSAPFieldType=GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.SAPFieldType");
            Type tSAPType = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.SAPType");

            try
            {
                MethodInfo mi = tInvoke.GetMethods(BindingFlags.Public | BindingFlags.Static)[0];
                conn.Open();
                object obj = mi.Invoke(null, new object[] { rfcName, conn });
                MethodInfo mi1 = tInvoke.GetMethod("DiscoverAPIMetaData", BindingFlags.NonPublic | BindingFlags.Instance);
                mi1.Invoke(obj, new object[] { rfcName });

                object inParamsObj = tInvoke.GetMethod("get_InParameters").Invoke(obj, null);
                object outParamsObj = tInvoke.GetMethod("get_OutParameters").Invoke(obj, null);
                object tableParamsObj = tInvoke.GetMethod("get_Tables").Invoke(obj, null);

                int inParamsCount = GetCollectionTypeCount(tRFCParameterCollection, inParamsObj);
                List<object> listInParams = new List<object>();
                for (int i = 0; i < inParamsCount; i++)
                {
                    object value = GetCollectionValueByIndex(tRFCParameterCollection, inParamsObj, i);
                    listInParams.Add(value);
                }
                List<RFCParameter> listIn = ConvertRFCParameterCollection(listInParams);


                int outParamsCount = GetCollectionTypeCount(tRFCParameterCollection, outParamsObj);
                List<object> listOutParams = new List<object>();
                for (int i = 0; i < outParamsCount; i++)
                {
                    object value = GetCollectionValueByIndex(tRFCParameterCollection, outParamsObj, i);
                    listOutParams.Add(value);
                }
                List<RFCParameter> listOut = ConvertRFCParameterCollection(listOutParams);

                DataSet ds = new DataSet();
                int tabsCount = GetCollectionTypeCount(tRFCParameterCollection, tableParamsObj);
                for (int i = 0; i < tabsCount; i++)
                {
                    object tabObj = tRFCParameterCollection.GetMethod("get_Item", new Type[] { typeof(Int32) }).Invoke(tableParamsObj, new object[] { i });
                    string name = (string)tRFCParameter.GetMethod("get_Name").Invoke(tabObj, null);
                    if (!ds.Tables.Contains(name))
                    {
                        object tab = tRFCParameter.GetMethod("get_Value").Invoke(tabObj, null);
                        object structure = tRFCTable.GetMethod("get_Columns").Invoke(tab, null);//tSAPStructureType.GetMethod("get_Item", new Type[] { typeof(Int32) }).Invoke(tab, null);
                        int columnsCount = GetCollectionTypeCount(tSAPStructureType, structure);
                        object field = tSAPStructureType.GetMethod("get_Item", new Type[] { typeof(Int32) }).Invoke(structure, new object[] { 0 });
                        string tabName = (string)tSAPFieldType.GetMethod("get_FieldStructureName").Invoke(field, null);
                        DataTable dt = Util.GetSructureDataTable.GetDataTable(tabName);
                        if (dt == null)
                        {
                            dt = new DataTable();
                            for (int j = 0; j < columnsCount; j++)
                            {
                                field = tSAPStructureType.GetMethod("get_Item", new Type[] { typeof(Int32) }).Invoke(structure, new object[] { j });
                                string fName = (string)tSAPType.GetMethod("get_Name").Invoke(field, null);
                                Type fType = (Type)tSAPType.GetMethod("get_Type").Invoke(field, null);
                                int fLength = (int)tSAPType.GetMethod("get_Length").Invoke(field, null);
                                string position = (string)tSAPFieldType.GetMethod("get_FieldPosition").Invoke(field, null);
                                DataColumn col = new DataColumn(fName, fType);
                                col.ExtendedProperties.Add("Length", fLength);
                                col.ExtendedProperties.Add("Position", position);
                                dt.Columns.Add(col);
                            }
                        }
                        dt.TableName = name;
                        ds.Tables.Add(dt);
                    }

                }

                RFCParametersInfo pInfo = new RFCParametersInfo(listOut, listIn, ds);

                conn.Close();
                return pInfo;
            }

            catch (Exception ex)
            {
                throw ex;
            }
            
           
        }

        private int GetCollectionTypeCount(Type t, object obj)
        {
            MethodInfo miCount = t.GetMethod("get_Count");
            if (miCount != null)
            {
                object objRet = miCount.Invoke(obj, null);
                return (int)objRet;
            }
            else
            {
                throw new Exception("can't not find method:get_Count(),in the type");
            }
        }

        private object GetCollectionValueByIndex(Type t, object obj, int index)
        {
            int count = GetCollectionTypeCount(t, obj);
            if (index > count)
            {
                throw new IndexOutOfRangeException();
            }
            else
            {
                MethodInfo miValue = t.GetMethod("get_Item", new Type[] { typeof(Int32) });
                if (miValue != null)
                {
                    object objRet = miValue.Invoke(obj, new object[] { index });
                    return objRet;
                }
                else
                {
                    throw new Exception("can't not find method:get_Item(int),in the type");
                }
            }
        }

        private List<RFCParameter> ConvertRFCParameterCollection(List<object> RFCParameterCollection)
        {
            Type tRFCParameterCollection = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.RFCParameter");
            List<RFCParameter> list = new List<RFCParameter>();
            foreach(object obj in RFCParameterCollection)
            {
                string name=(string)tRFCParameterCollection.GetMethod("get_Name").Invoke(obj,null);
                if (!list.Exists(item=>item.Name==name))
                {
                    object sapType = tRFCParameterCollection.GetMethod("get_SAPType").Invoke(obj, null);
                    RFCTYPE rfcType = GetRFCType(sapType);
                    bool isDefault = (bool)tRFCParameterCollection.GetMethod("get_IsDefault").Invoke(obj, null);
                    bool isOptional = (bool)tRFCParameterCollection.GetMethod("get_IsOptional").Invoke(obj, null);
                    int position = (int)tRFCParameterCollection.GetMethod("get_Position").Invoke(obj, null);
                    RFCParameter pInfo = new RFCParameter(name, position, isDefault, isOptional, rfcType);
                    list.Add(pInfo);
                }
            }
            return list;

        }

        private RFCTYPE GetRFCType(object SAPType)
        {
            Type tSAPType = GetTypeFromSAPAdapter("Microsoft.Adapter.SAP.SAPType");
            MethodInfo miGetName = tSAPType.GetMethod("get_RfcType");
            return (RFCTYPE)miGetName.Invoke(SAPType, null);
        }
    }

    public class RFCParametersInfo
    {
        private List<RFCParameter> _listOut;
        private List<RFCParameter> _listIn;
        private DataSet _dtParams;

        public RFCParametersInfo(List<RFCParameter> listOut, List<RFCParameter> listIn, DataSet dtParams)
        {
            this._listIn = listIn;
            this._listOut = listOut;
            this._dtParams = dtParams;
        }

        public List<RFCParameter> InParams
        {
            get { return this._listIn; }
            set { this._listIn = value; }
        }

        public List<RFCParameter> OutParams
        {
            get { return this._listOut; }
            set { this._listOut = value; }
        }

        public DataSet TableParams
        {
            get { return this._dtParams; }
            set { this._dtParams = value; }
        }




    }

    public class RFCParameter
    {
        private string _name;
        private RFCTYPE _type;
         private bool _isDefault;
        private bool _isOptional;
        private int _position;
        private object _value;

        public RFCParameter(string Name, int Position, bool IsDefault, bool IsOptional,RFCTYPE Type)
        {
            this._name=Name;
            this._position=Position;
            this._isDefault=IsDefault;
            this._isOptional=IsOptional;
            this._type = Type;
        }

        

        public string Name
        {
            get{return this._name;}
        }

        public RFCTYPE Type
        {
            get { return _type; }
        }

        public bool IsDefault
        {
            get{return this._isDefault;}
        }

        public bool IsOptional
        {
            get{return this._isOptional;}
        }

        public int Position
        {
            get{return this._position;}
        }

        public object Value
        {
            get{return this._value;}
            set
            {
                this._value = value;
            }
        }
    }

    public enum RFCTYPE
    {
        RFCTYPE_CHAR = 0,
        RFCTYPE_DATE = 1,
        RFCTYPE_BCD = 2,
        RFCTYPE_TIME = 3,
        RFCTYPE_BYTE = 4,
        RFCTYPE_ITAB = 5,
        RFCTYPE_NUM = 6,
        RFCTYPE_FLOAT = 7,
        RFCTYPE_INT = 8,
        RFCTYPE_INT2 = 9,
        RFCTYPE_INT1 = 10,
        RFCTYPE_DATE_1 = 12,
        RFCTYPE_DATE_2 = 13,
        RFCTYPE_NULL = 14,
        _RFCTYPE_differ_from_abap = 14,
        RFCTYPE_WIDE_2 = 15,
        RFCTYPE_WCHAR = 15,
        _RFCTYPE_illegal_wchar = 16,
        RFCTYPE_WIDE_4 = 16,
        RFCTYPE_STRUCTURE = 17,
        RFCTYPE_ABAP4OBJECT = 18,
        RFCTYPE_IUNKNOWN = 19,
        RFCTYPE_WSTRING = 20,
        RFCTYPE_SAPAUTOMATION = 21,
        RFCTYPE_STUB = 22,
        RFCTYPE_WIRE_OBJECT = 23,
        RFCTYPE_BLOB = 24,
        RFCTYPE_CORBA_OBJECT = 25,
        RFCTYPE_PADDING = 26,
        RFCTYPE_UNICODE = 27,
        RFCTYPE_XMLDATA = 28,
        RFCTYPE_STRING = 29,
        RFCTYPE_XSTRING = 30,
        _RFCTYPE_max_value = 31,
    }
}
